package com.naiterui.ehp.bs.doctor.service.record.impl;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.naiterui.common.redis.RedisUtil;
import com.naiterui.ehp.bp.constants.CommonConstant;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.utils.date.DateUtil;
import com.naiterui.ehp.bp.utils.encypt.GengerCode;
import com.naiterui.ehp.bp.utils.encypt.MD5;
import com.naiterui.ehp.bp.utils.net.RestHttpUtil;
import com.naiterui.ehp.bs.doctor.domain.RecordUserInfo;
import com.naiterui.ehp.bs.doctor.domain.RecordWorkContract;
import com.naiterui.ehp.bs.doctor.domain.RecordWorkInfo;
import com.naiterui.ehp.bs.doctor.domain.StandardDict;
import com.naiterui.ehp.bs.doctor.exception.ExceptionCodes;
import com.naiterui.ehp.bs.doctor.repository.IRecordWorkContractRepository;
import com.naiterui.ehp.bs.doctor.repository.IRecordWorkInfoRepository;
import com.naiterui.ehp.bs.doctor.repository.IStandardDictRepository;
import com.naiterui.ehp.bs.doctor.service.record.ISupervisePlatformService;
import com.naiterui.ehp.bs.doctor.vo.record.RecordHospitalVO;
import com.naiterui.ehp.bs.doctor.vo.record.RecordInfoVO;
import com.naiterui.ehp.bs.doctor.vo.record.RecordUserInfoVO;
import com.naiterui.ehp.bs.doctor.vo.record.ReocrdUserBaseInfoVO;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

/**
 * 互联网医院备案平台-海南
 *
 * @author guoyongxiang
 * @date 2019/12/28 14:07
 * @since 1.0.0
 */
@Slf4j
@Service
public class SupervisePlatformHaiNanServiceImpl implements ISupervisePlatformService {

    private static final String API_GET_HOS_TOKEN = "/jgpt/secure/get_hos_token";
    private static final String API_SYNC_USER = "/jgpt/services/up_doc_basic_info";
    private static final String RDS_SUPERVISE_TOKEN = CommonConstant.REDIS_PRE_DR + "supervise_token_";
    @Autowired
    private RecordHospitalVO recordHospitalVO;
    @Value("${file.base.prefix}")
    private String basePath;
    private static final String ORG_IMG = "/recommend/static/EHP_NTR.PNG";

    @Autowired
    private IRecordWorkContractRepository recordWorkContractRepository;
    @Autowired
    private IRecordWorkInfoRepository recordWorkInfoRepository;
    @Autowired
    private IStandardDictRepository standardDictRepository;

    @Override
    public void syncRecordInfo(String userCode, Integer userType, RecordInfoVO recordInfoVO) throws BusinessException {
        ReocrdUserBaseInfoVO userBaseInfoVO = recordInfoVO.getUserBaseInfo();
        RecordUserInfoVO userInfoVO = recordInfoVO.getUserInfo();

        Map<String, Object> param = this.getBaseParams();
        param.put("in_doc_code", userCode);
        if (RecordUserInfo.USER_TYPE_DR.equals(userType)) {
            param.put("doc_type", "诊疗医师");
        } else {
            param.put("doc_type", "审方药师");
        }
        param.put("doc_name", userBaseInfoVO.getName());

        // 性别 1:男 2:女
        String geCode;
        String geName;
        if (Integer.parseInt(userBaseInfoVO.getIdCardNum().substring(16).substring(0, 1)) % 2 == 0) {// 判断性别
            geCode = "2";
            geName = "女";
        } else {
            geCode = "1";
            geName = "男";
        }
        param.put("ge_code", geCode);
        param.put("ge_name", geName);

        StandardDict standardDict = this.standardDictRepository.findByCodeAndType(userInfoVO.getNationCode(), StandardDict.TYPE_NATION);
        param.put("nation_code", userInfoVO.getNationCode());
        param.put("nation_name", standardDict.getName());
        param.put("doc_birthdate", this.getBirthday(userBaseInfoVO.getIdCardNum()));
        // 职称
        param.put("title_code", TitleEnum.getCodeByInternalCode(userType, userBaseInfoVO.getTitleId()).getCode());
        param.put("title_name", TitleEnum.getCodeByInternalCode(userType, userBaseInfoVO.getTitleId()).getName());
        param.put("doc_address", userInfoVO.getAddress());
        param.put("doc_postal_code", userInfoVO.getZipCode());
        param.put("health_condition", userInfoVO.getHealth());
        param.put("doc_edu", this.getEducationName(userInfoVO.getEducation()));
        param.put("appraisal_content", userInfoVO.getAppraisal());
        param.put("punish_content", userInfoVO.getPunish());
        param.put("other_content", userInfoVO.getOther());
        param.put("work_experience_list", this.getWorkInfoList(userCode));
        // 医师第一执业机构编码
        param.put("work_inst_code", userInfoVO.getHospitalCode());
        // 医师第一执业机构名称
        param.put("work_inst_name", userInfoVO.getHospitalName());
        param.put("doc_tel", userBaseInfoVO.getPhone());

        param.put("id_card", userBaseInfoVO.getIdCardNum().toUpperCase());
        // 执业证
        param.put("prac_no", userBaseInfoVO.getPracNum());
        param.put("prac_rec_date", DateUtil.formatDate(userInfoVO.getPracIssue(), DateUtil.DATE_FORMAT));
        // 资格证
        param.put("cert_no", userBaseInfoVO.getCertNum());
        param.put("cert_rec_date", DateUtil.formatDate(userInfoVO.getCertIssue(), DateUtil.DATE_FORMAT));
        // 执业级别
        param.put("prac_level", userInfoVO.getPracLevel());
        // 执业类别
        param.put("prac_type", userInfoVO.getPracType());
        // 执业范围
        param.put("prac_scope_approval", userInfoVO.getPracScopeApproval());
        param.put("agree_terms", "是");
        // 执业有效期起止时间
        param.put("doc_multi_sited_date_start", DateUtil.formatDate(userInfoVO.getPracStartDate(), DateUtil.DATE_FORMAT));
        param.put("doc_multi_sited_date_end", DateUtil.formatDate(userInfoVO.getPracEndDate(), DateUtil.DATE_FORMAT));
        param.put("hos_opinion", userInfoVO.getAuditOpinion());
        // 机构电子章 base64
        param.put("hos_digital_sign", encryptToBase64(this.basePath + ORG_IMG));
        // 机构意见时间
        param.put("hos_opinion_date", DateUtil.formatDate(userInfoVO.getApplyDate(), DateUtil.DATE_FORMAT));
        // 聘用合同
        param.put("employ_files_list", this.getWorkContractBase64List(userCode));
        // 医生数字签名 base64
        param.put("digital_sign", encryptToBase64(this.basePath + userBaseInfoVO.getSealImage()));
        // 身份证照片文件列表
        List<Map<String, String>> idCardList = this.getBase64List("id_card", userBaseInfoVO.getIdCardUrls());
        param.put("id_card_list", idCardList);

        // 医师执业证文件列表
        List<Map<String, String>> pracList = this.getBase64List("cert_doc_prac", userBaseInfoVO.getPracUrls());
        param.put("cert_doc_prac_list", pracList);

        // 医师资格证文件列表
        List<Map<String, String>> certList = this.getBase64List("doc_cert", userBaseInfoVO.getCertUrls());
        param.put("doc_cert_list", certList);
        // 医师认证照片文件
        param.put("doc_photo", encryptToBase64(this.basePath + userBaseInfoVO.getHeadUrl()));
        param.put("updated_last", DateUtil.formatDate(new Date(), DateUtil.DATE_TIME_FORMAT));

        Map<String, String> header = new HashMap<>();
        header.put("hisId", this.recordHospitalVO.getHisId());
        header.put("uname", this.recordHospitalVO.getUname());
        header.put("hosToken", this.getToken());
        Map<String, String> data = new HashMap<>();
        data.put("data", JSON.toJSONString(param));
        ResponseEntity<String> responseEntity = RestHttpUtil.post()
                                                            .connectTimeout(20000)
                                                            .readTimeout(10000)
                                                            .url(this.recordHospitalVO.getApiHost() + API_SYNC_USER)
                                                            .header(header)
                                                            .paramMap(data)
                                                            .execute(new ParameterizedTypeReference<String>() {
                                                            });
        log.info("备案平台，调用结果 code:{}, data:{}", responseEntity.getStatusCodeValue(), responseEntity.getBody());
        if (responseEntity.getStatusCodeValue() == 200) {
            JSONObject jsonObject = JSON.parseObject(responseEntity.getBody());
            String status = jsonObject.getString("status");
            if ("0".equals(status)) {
                String msg = jsonObject.getString("errorType");
                log.warn("调用备案平台失败 userCode:{}, errorType:{}", userCode, msg);
                throw new BusinessException(ExceptionCodes.FAILED, "调用备案平台失败: " + msg);
            }
        } else {
            log.warn("调用备案平台异常 userCode:{}, responseEntity:{}", userCode, responseEntity.getBody());
            throw new BusinessException(ExceptionCodes.FAILED, "调用备案平台异常");
        }

    }

    @Override
    public boolean callbackVerify(String hisId, String uname, String pwd) {
        if (!this.recordHospitalVO.getHisId().equals(hisId)) {
            return false;
        }
        if (!this.recordHospitalVO.getUname().equals(uname)) {
            return false;
        }
        if (!this.recordHospitalVO.getUpass().equals(pwd)) {
            return false;
        }
        return true;
    }

    private String getToken() {
        String tokenJsonStr = RedisUtil.valueOps().getString(RDS_SUPERVISE_TOKEN + this.recordHospitalVO.getHisId());
        if (StringUtils.isBlank(tokenJsonStr)) {
            tokenJsonStr = this.refreshToken();
        }
        JSONObject jsonObject = JSON.parseObject(tokenJsonStr);
        return jsonObject.getString("hosToken");
    }

    public String refreshToken() {
        String random = GengerCode.generateSaltCode();
        String timeStamp = System.currentTimeMillis() / 1000 + "";

        Map<String, String> map = new HashMap<>();
        map.put("hisId", this.recordHospitalVO.getHisId());
        map.put("uname", this.recordHospitalVO.getUname());
        map.put("random", random);
        map.put("timeStamp", timeStamp);
        String hash = this.getTokenHash(random, timeStamp);
        map.put("hash", hash);
        String param = JSON.toJSONString(map);
        ResponseEntity<String> responseEntity = RestHttpUtil.post()
                                                            .url(this.recordHospitalVO.getApiHost() + API_GET_HOS_TOKEN)
                                                            .body(param)
                                                            .execute(new ParameterizedTypeReference<String>() {
                                                            });

        String responseEntityBody = responseEntity.getBody();
        JSONObject jsonObject = JSON.parseObject(responseEntityBody);
        // 毫秒
        Integer validSeconds = jsonObject.getIntValue("validSeconds");
        // token 信息存储缓存，并提前600秒过期
        RedisUtil.valueOps().setString(RDS_SUPERVISE_TOKEN + this.recordHospitalVO.getHisId(), responseEntityBody, validSeconds / 1000 - 600);
        log.info("备案平台，刷新hisId:{}, token: {}", this.recordHospitalVO.getHisId(), responseEntityBody);
        return responseEntityBody;
    }

    private String getTokenHash(String random, String timeStamp) {
        String[] arr = {this.recordHospitalVO.getHisId(), this.recordHospitalVO.getUname(), this.recordHospitalVO.getUpass(), random, timeStamp};
        Arrays.sort(arr);
        String joinStr = Stream.of(arr).collect(Collectors.joining());
        return MD5.md5(joinStr);
    }


    private Map<String, Object> getBaseParams() {

        Map<String, Object> map = new HashMap<>();
        map.put("org_code", this.recordHospitalVO.getOrgCode());
        map.put("org_name", this.recordHospitalVO.getOrgName());
        map.put("org_reg_no", this.recordHospitalVO.getOrgRegNo());
        map.put("org_address", this.recordHospitalVO.getOrgAddress());
        map.put("org_postal_code", this.recordHospitalVO.getOrgPostalCode());
        map.put("org_tel", this.recordHospitalVO.getOrgTel());
        return map;
    }

    @Builder
    @Data
    static class WorkExperience {
        /**
         * 工作经历开始时间
         */
        private String we_date_start;
        /**
         * 工作经历结束时间
         */
        private String we_date_end;
        /**
         * 工作经历单位名称
         */
        private String we_employer;
        /**
         * 工作经历技术职务
         */
        private String we_title;
        /**
         * 工作经历证明人
         */
        private String we_certifier;
    }

    @Getter
    @AllArgsConstructor
    enum TitleEnum {

        DR_PHYSICIAN("1", "医师", RecordUserInfo.USER_TYPE_DR, 0),
        DR_RESIDENT("3", "住院医师", RecordUserInfo.USER_TYPE_DR, 4),
        DR_ATTENDING_SURGEON("4", "主治医师", RecordUserInfo.USER_TYPE_DR, 3),
        DR_ASSOCIATE_CHIEF_PHYSICIAN("5", "副主任医师", RecordUserInfo.USER_TYPE_DR, 1),
        DR_CHIEF_PHYSICIAN("6", "主任医师", RecordUserInfo.USER_TYPE_DR, 2),
        PH_ASSISTANT_PHARMACIST("7", "药士", RecordUserInfo.USER_TYPE_PH, 1),
        PH_PHARMACIST("8", "药师", RecordUserInfo.USER_TYPE_PH, 2),
        PH_PHARMACEUTIST_IN_CHARGE("9", "主管药师", RecordUserInfo.USER_TYPE_PH, 3),
        PH_ASSOCIATE_CHIEF_PHARMACEUTIST("10", "副主任药师", RecordUserInfo.USER_TYPE_PH, 4),
        PH_CHIEF_PHARMACEUTIST("11", "主任药师", RecordUserInfo.USER_TYPE_PH, 5);

        /**
         * 监管平台编码
         */
        private String code;
        /**
         * 职称
         */
        private String name;
        /**
         * 角色类别
         */
        private Integer type;
        /**
         * 内部编码
         */
        private Integer internalCode;

        public static TitleEnum getCodeByInternalCode(Integer type, Integer internalCode) {
            TitleEnum[] titles = TitleEnum.values();
            for (TitleEnum title : titles) {
                if (title.getType().equals(type) && title.getInternalCode().equals(internalCode)) {
                    return title;
                }
            }
            return null;
        }
    }

    private String getBirthday(String idCardNum) {
        String year = idCardNum.substring(6).substring(0, 4);
        String month = idCardNum.substring(10).substring(0, 2);
        String day = idCardNum.substring(12).substring(0, 2);
        return year + "-" + month + "-" + day;
    }

    public static String encryptToBase64(String filePath) {
        if (filePath == null) {
            return null;
        }
        try {
            byte[] b = Files.readAllBytes(Paths.get(filePath));
            return Base64.getEncoder().encodeToString(b);
        } catch (IOException e) {
            log.error("读取文件转base64异常", e);
        }
        return null;
    }

    public List<Map<String, String>> getBase64List(String key, List<String> urls) {
        List<Map<String, String>> list = new ArrayList<>();
        for (String l : urls) {
            Map<String, String> map = new HashMap<>();
            map.put(key, encryptToBase64(this.basePath + l));
            list.add(map);
        }
        return list;
    }

    public List<Map<String, String>> getWorkContractBase64List(String userCode) {
        List<RecordWorkContract> workContracts = this.recordWorkContractRepository.findAllByUserCode(userCode);
        List<Map<String, String>> list = new ArrayList<>();
        for (RecordWorkContract w : workContracts) {
            Map<String, String> map = new HashMap<>();
            map.put("employ_files", encryptToBase64(this.basePath + w.getUrl()));
            list.add(map);
        }
        return list;
    }

    public List<WorkExperience> getWorkInfoList(String userCode) {
        List<WorkExperience> list = new ArrayList<>();
        List<RecordWorkInfo> workInfos = this.recordWorkInfoRepository.findByUserCodeOrderByEndDateDesc(userCode);
        if (workInfos != null && workInfos.size() > 0) {
            RecordWorkInfo workInfo = workInfos.get(0);
            list.add(WorkExperience.builder()
                                   .we_employer(workInfo.getEmployer())
                                   .we_certifier(workInfo.getCertifier())
                                   .we_title(workInfo.getTitle())
                                   .we_date_start(DateUtil.formatDate(workInfo.getStartDate(), DateUtil.DATE_FORMAT))
                                   .we_date_end(DateUtil.formatDate(workInfo.getEndDate(), DateUtil.DATE_FORMAT))
                                   .build());
        }
        return list;
    }

    public String getEducationName(Integer education) {
        String name = "";
        switch (education) {
            case 1:
                name = "无";
                break;

            case 2:
                name = "传统医学师承和确有专长";
                break;

            case 3:
                name = "中专";
                break;

            case 4:
                name = "专科";
                break;

            case 5:
                name = "本科";
                break;

            case 6:
                name = "硕士研究生";
                break;

            case 7:
                name = "博士研究生";
                break;
            default:
                name = "无";
        }
        return name;
    }

}
